package org.dreamwork.jasmine2.engine;

import com.lowagie.text.DocumentException;
import org.dreamwork.jasmine2.web.controls.Page;
import org.dreamwork.jasmine2.web.controls.pdf.DocumentInfo;
import org.dreamwork.jasmine2.web.controls.pdf.PDFFont;
import org.dreamwork.jasmine2.web.controls.pdf.controls.AbstractPDFContainer;
import org.dreamwork.util.StringUtil;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import org.xhtmlrenderer.simple.extend.XhtmlNamespaceHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.net.URL;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2010-10-14
 * Time: 12:01:54
 */
public class PDFProcessor {
    private File basedir;

    PDFProcessor (File basedir) {
        this.basedir = basedir;
    }

    public void processPDF (Page page, String content) throws IOException, DocumentException, SAXException, ParserConfigurationException {
        HttpContext context = HttpContext.current ();
        HttpServletRequest request = context.getRequest ();
        HttpServletResponse response = context.getResponse ();
        ITextRenderer renderer = new ITextRenderer ();
        StringBuffer path = request.getRequestURL ();
        String service = request.getServletPath ();
        String base = path.substring (0, path.length () - service.length ());
        if (!base.endsWith ("/")) base += '/';
        renderer.getSharedContext ().setNamespaceHandler (new XhtmlNamespaceHandler ());
        URLUserAgent uac = new URLUserAgent (renderer.getOutputDevice ());
        uac.setBase (base);
        uac.setSharedContext (renderer.getSharedContext ());
        uac.setContext (request.getContextPath ());
        renderer.getSharedContext ().setUserAgentCallback (uac);
        renderer.setDocument (convert (content), null);

        processPdfFonts (renderer);
        renderer.layout ();
        response.setContentType (page.getContentType ());
        renderer.createPDF (response.getOutputStream ());
        renderer.finishPDF ();
    }

    private void processPdfFonts (ITextRenderer renderer) throws IOException, DocumentException {
        DocumentInfo pdfInfo =
                (DocumentInfo) HttpContext.current ().getAttribute (AbstractPDFContainer.PDF_Document_Info_Key);
        if (pdfInfo == null) return;
        if (pdfInfo.getFonts ().size () > 0) {
            ITextFontResolver fontResolver = renderer.getFontResolver ();
            ServletContext context = HttpContext.current ().getApplication ();
            for (PDFFont font : pdfInfo.getFonts ()) {
                String url = font.getUrl ();
                if (StringUtil.isEmpty (url))
                    throw new RuntimeException ("Illegal Font URL: " + url);
                url = url.trim ();
                File file = new File (url);

                if (file.canRead ())
                    fontResolver.addFont (file.getAbsolutePath (), "Identity-H", false);
                else {
                    URL u = url.charAt (0) == '/' ?
                            context.getResource (url) :
                            getClass ().getClassLoader ().getResource (url);
                    if (u != null) {
                        String fileName = getFontCache (u);
                        fontResolver.addFont (fileName, "Identity-H", false);
                    } else
                        throw new IOException ("Can't read font url : " + url);
                }
            }
        }
    }

    private String getFontCache (URL url) throws IOException {
        if (url.getProtocol ().equals ("file")) {
            return url.getFile ();
        }

        String path  = url.toString ();
        String lower = path.toLowerCase ();
        if (lower.startsWith ("file:/")) return path.substring ("file:/".length ());

        File fontDir = new File (basedir, "fonts");
        if (!fontDir.exists ()) fontDir.mkdirs ();

        String protocol = url.getProtocol ();
        String fileName;
        if (protocol.equalsIgnoreCase ("jar")) {
            int pos = path.lastIndexOf ('!');
            String tmp = path.substring (pos + 1);
            pos = tmp.lastIndexOf ('/');
            if (pos != -1) tmp = tmp.substring (pos + 1);
            fileName = tmp;
        } else {
            int pos = path.lastIndexOf ('/');
            fileName = path.substring (pos + 1);
        }
        File file = new File (fontDir, fileName);
        if (!file.exists ()) {
            byte[] buff = new byte[1024];
            FileOutputStream fos = new FileOutputStream (file);
            InputStream is = url.openStream ();
            try {
                int length;
                while ((length = is.read (buff)) != -1)
                    fos.write (buff, 0, length);
            } finally {
                fos.flush ();
                fos.close ();
                if (is != null) is.close ();
            }
        }
        return file.getAbsolutePath ();
    }

    private Document convert (String content) throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();
        return dbf.newDocumentBuilder ().parse (new InputSource (new StringReader (content)));
    }
}